package com.lm.activiti.boot.annotation.parser;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.lm.activiti.boot.annotation.OuterHandler;
import com.lm.activiti.boot.annotation.ex.DataException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author lm_jcf
 * @date 2020/10/27 11:09
 */
@Aspect
@Component
@Slf4j
public class OuterHandlerParser {

    private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();;

    @Pointcut("@annotation(com.lm.activiti.boot.annotation.OuterHandler)")
    void pointCut() {

    }

    /**
     * joinPoint.getTarget() = 切面目标对象实例
     * joinPoint.getThis() = 代理对象
     *
     */

    @Before("pointCut()")
    void before(JoinPoint joinPoint) throws InvocationTargetException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InstantiationException {

        log.info("--------------切面开始------------");
        if (!Objects.nonNull(joinPoint)) {
            return;
        }

        /*log.info("参数：" + JSON.toJSONString(joinPoint.getArgs()));

        log.info("方法：" + joinPoint.getSignature());

        Class<?> classs = joinPoint.getTarget().getClass(); // 当前类的对象

        System.out.println(classs.getName());

        for (Annotation annotation : classs.getAnnotations()) {
            log.info("注解：" + annotation);
        }


        Class<?> aClass = joinPoint.getThis().getClass();
        System.out.println(aClass.getName());

        Class declaringType = joinPoint.getSignature().getDeclaringType();
        for (Annotation annotation : declaringType.getAnnotations()) {
            log.info("注解2：" +annotation);
        }*/

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        System.out.println(JSON.toJSONString(signature.getParameterNames()));

        Object[] args = joinPoint.getArgs();

        System.out.println(JSON.toJSONString(args));

        String annoMethodName = joinPoint.getSignature().getName();

        Class<?> clazz_main = joinPoint.getThis().getClass();

        System.out.println("方法相关:"+ JSON.toJSONString(signature));

        for (Method method : clazz_main.getMethods()) {
            if (!method.getName().equals(annoMethodName)) {
                continue;
            }
            String[] parameterNames = signature.getParameterNames();
            Object[] parameterValues = joinPoint.getArgs();

            List<String> parameterList = Lists.newArrayList();
            for (int i = 0; i < parameterNames.length; i++) {
                String name = parameterNames[i];
                Object value = parameterValues[i];
                parameterList.add(name + "=" + (Objects.nonNull(value) ? JSON.toJSONString(value) : "null"));
            }


            OuterHandler outerHandler = method.getAnnotation(OuterHandler.class);
            org.slf4j.Logger logger = LoggerFactory.getLogger(clazz_main);
            if (Objects.nonNull(logger)) {
                logger.info(outerHandler.message() + " parameters：" + StringUtils.join(parameterList, "、"));
            }

            System.out.println("------------------------");
        }


        System.out.println("\n\n");
    }

    @AfterThrowing(pointcut = "pointCut()", throwing = "e")
    Object afterException(JoinPoint joinPoint, Exception e) throws Throwable {

        log.error("-----触发异常。-----");

        if (e instanceof DataException) {
            DataException dataException = (DataException) e;
            System.out.println(dataException.getErrorMessage() + dataException.getErrorCode());

        } else {
            log.error(e.getMessage());
        }

        System.out.println("\n\n");
        return "数据还是不存在";
    }

    @Around("pointCut()")
    Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Exception e) {
            proceed = "数据不存在";
        }


        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        Method method = signature.getMethod();

//        this.validateConstraintForMethod(joinPoint.getThis(), method, joinPoint.getArgs(), signature);

        Object[] parameters = joinPoint.getArgs();

        for (Object parameter : parameters) {
            if (Objects.nonNull(parameter)) {

                System.out.println(parameter+"参数类型：" + parameter.getClass().isPrimitive());
            }

            this.validateConstraint(parameter);
        }

        // 参数注解: 1维是参数、2维是注解

        /*Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            Object parameter = parameters[i];
            Annotation[] parameterAnn = parameterAnnotations[i];

            if (Objects.isNull(parameterAnn)) {
                // 参数为空
                continue;
            }

            for (Annotation pAnnotation : parameterAnn) {
//                javax.validation.constraints

                if (!pAnnotation.annotationType().getName().startsWith("javax.validation.constraints")) {
                    continue;
                }

                Method messageMethod = pAnnotation.getClass().getDeclaredMethod("message");

                Object message = messageMethod.invoke(pAnnotation, null);


                System.out.println("注解message:" + message);


            }

        }*/

        // 参数对象的注解：



        log.info(JSON.toJSONString(proceed));
        return proceed;
    }

    private void validateConstraint(Object object){
        if (Objects.isNull(object)) {
            return;
        }

        System.out.println("\n\n --方法对象--");
//        HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();

        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

        // 根据 validatorFactory 拿到一个 Validator

        Validator validator = validatorFactory.getValidator();

        Set<ConstraintViolation<Object>> validate = validator.validate(object);

        validate.stream().map(result -> result.getPropertyPath() + " " + result.getMessage() + ": " + result.getInvalidValue()).forEach(System.out::println);
    }

    private void validateConstraintForMethod(Object object, Method method, Object[] args,MethodSignature signature) {

        System.out.println("\n\n 方法级别---");
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

        ExecutableValidator executableValidator = validatorFactory.getValidator().forExecutables();

        Set<ConstraintViolation<Object>> resultSet = executableValidator.validateParameters(object, method, args);


        String[] parameterNames = signature.getParameterNames();

        for (ConstraintViolation<Object> violation : resultSet) {

            System.out.println("执行参数");
            if (Objects.isNull(violation.getPropertyPath())) continue;

            String field = violation.getPropertyPath().toString();
            String parameterIndex = field.substring(field.length() - 1, field.length());

            String parameterName = parameterNames[Integer.valueOf(parameterIndex)];

            System.out.println(parameterName + "=" + violation.getMessage());

            System.out.println("参数类路径：" + violation.getRootBeanClass().getPackage().getName());
        }

        System.out.println("\n --");
        resultSet.stream().map(result -> result.getPropertyPath() + " " + result.getMessage() + ": " + result.getInvalidValue()).forEach(System.out::println);

        System.out.println("\n\n");
    }
}

